#pragma once
#include "../stdafx.h"
#include "meshGeo.h"
#include <stdexcept>

#define NAME_D3D12_OBJECT(x) SetName((x).Get(), L#x)

inline float clamp(float x, float min_x, float max_x)
{
    if (x < min_x) return min_x;
    if (x > max_x) return max_x;
    return x;
}

inline void SetName(ID3D12Object* pObject, LPCWSTR name)
{
    pObject->SetName(name);
}

inline std::string HrToString(HRESULT hr)
{
    char s_str[64] = {};
    sprintf_s(s_str, "HRESULT of 0x%08X", static_cast<UINT>(hr));
    return std::string(s_str);
}

class HrException : public std::runtime_error
{
public:
    HrException(HRESULT hr) : std::runtime_error(HrToString(hr)), m_hr(hr) {}
    HRESULT Error() const { return m_hr; }
private:
    const HRESULT m_hr;
};

inline void ThrowIfFailed(HRESULT hr)
{
	if (FAILED(hr))
		throw HrException(hr);
}

struct D3DUtil
{
    static ComPtr<ID3D12Resource> CreateDefaultBuffer(
        ID3D12Device* device, ID3D12GraphicsCommandList* cmdList,
        const void* initData, UINT64 byteSize, ComPtr<ID3D12Resource>& uploadBuffer,
        D3D12_RESOURCE_STATES dstState);
    static UINT CalConstBufferSize(UINT);
};

template<typename T>
class UploadBuffer
{
public:
    UploadBuffer(ID3D12Device* device, UINT elemCnt, bool isConstBuff):
        m_isConstBuff(isConstBuff)
    {
        if (isConstBuff)
            m_elemByteSize = D3DUtil::CalConstBufferSize(sizeof(T));
        else
            m_elemByteSize = sizeof(T);

        ThrowIfFailed(device->CreateCommittedResource(
                &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
                D3D12_HEAP_FLAG_NONE,
                &CD3DX12_RESOURCE_DESC::Buffer(static_cast<UINT64>(m_elemByteSize * elemCnt)),
                D3D12_RESOURCE_STATE_GENERIC_READ,
                nullptr,
                IID_PPV_ARGS(&m_uploadBuffer)));
        //get a CPU pointer to the buffer
        ThrowIfFailed(m_uploadBuffer->Map(0, nullptr, reinterpret_cast<void**>(&m_mappedData)));
    }

    UploadBuffer(const UploadBuffer&) = delete;
    UploadBuffer& operator=(const UploadBuffer&) = delete;

    ~UploadBuffer()
    {
        if (m_uploadBuffer != nullptr)
            m_uploadBuffer->Unmap(0, nullptr);
        m_mappedData = nullptr;
    }

    ID3D12Resource* Resource() const
    {
        return m_uploadBuffer.Get();
    }

    void CopyData(int elemIndex, const T& data)
    {
        memcpy(&m_mappedData[elemIndex * m_elemByteSize], &data, sizeof(T));
    }

private:
    ComPtr<ID3D12Resource> m_uploadBuffer;
    UINT8* m_mappedData;

    bool m_isConstBuff;
    UINT m_elemByteSize;

};

